{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "83fcadf3",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-2/chatbot-summarization.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239436-lesson-5-chatbot-w-summarizing-messages-and-memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b651ead9-5504-45ee-938d-f91ac78dddd1",
   "metadata": {},
   "source": [
    "# Chatbot with message summarization\n",
    "\n",
    "## Review\n",
    "\n",
    "We've covered how to customize graph state schema and reducer. \n",
    " \n",
    "We've also shown a number of ways to trim or filter messages in graph state. \n",
    "\n",
    "## Goals\n",
    "\n",
    "Now, let's take it one step further! \n",
    "\n",
    "Rather than just trimming or filtering messages, we'll show how to use LLMs to produce a running summary of the conversation.\n",
    " \n",
    "This allows us to retain a compressed representation of the full conversation, rather than just removing it with trimming or filtering.\n",
    "\n",
    "We'll incorporate this summarization into a simple Chatbot.  \n",
    "\n",
    "And we'll equip that Chatbot with memory, supporting long-running conversations without incurring high token cost / latency. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "000a6daa-92ad-4e57-a060-d1c81176eb0d",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langchain_core langgraph langchain_openai"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "09201a62",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfddfce9-3a9b-4b35-a76d-28265515aabd",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "We'll use [LangSmith](https://docs.langchain.com/langsmith/home) for  [tracing](https://docs.langchain.com/langsmith/observability-concepts)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "464856d4",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "_set_env(\"LANGSMITH_API_KEY\")\n",
    "os.environ[\"LANGSMITH_TRACING\"] = \"true\"\n",
    "os.environ[\"LANGSMITH_PROJECT\"] = \"langchain-academy\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "537ade30-6a0e-4b6b-8bcd-ce90790b6392",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "model = ChatOpenAI(model=\"gpt-4o\",temperature=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db3afac3-8b7a-45db-a3c1-7e4125c1bc8b",
   "metadata": {},
   "source": [
    "We'll use `MessagesState`, as before.\n",
    "\n",
    "In addition to the built-in `messages` key, we'll now include a custom key (`summary`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "948e60f0-5c76-4235-b40e-cf523205d40e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import MessagesState\n",
    "class State(MessagesState):\n",
    "    summary: str"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6855ea31-5cc1-4277-a189-0b72459f67ec",
   "metadata": {},
   "source": [
    "We'll define a node to call our LLM that incorporates a summary, if it exists, into the prompt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c3f7d19b-afe0-4381-9b1a-0a832b162e7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import SystemMessage, HumanMessage, RemoveMessage\n",
    "\n",
    "# Define the logic to call the model\n",
    "def call_model(state: State):\n",
    "    \n",
    "    # Get summary if it exists\n",
    "    summary = state.get(\"summary\", \"\")\n",
    "\n",
    "    # If there is summary, then we add it\n",
    "    if summary:\n",
    "        \n",
    "        # Add summary to system message\n",
    "        system_message = f\"Summary of conversation earlier: {summary}\"\n",
    "\n",
    "        # Append summary to any newer messages\n",
    "        messages = [SystemMessage(content=system_message)] + state[\"messages\"]\n",
    "    \n",
    "    else:\n",
    "        messages = state[\"messages\"]\n",
    "    \n",
    "    response = model.invoke(messages)\n",
    "    return {\"messages\": response}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6882042c-b42d-4d52-a6a7-6ec8efa72450",
   "metadata": {},
   "source": [
    "We'll define a node to produce a summary.\n",
    "\n",
    "Note, here we'll use `RemoveMessage` to filter our state after we've produced the summary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "78c7aa59-3760-4e76-93f1-bc713e3ec39e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def summarize_conversation(state: State):\n",
    "    \n",
    "    # First, we get any existing summary\n",
    "    summary = state.get(\"summary\", \"\")\n",
    "\n",
    "    # Create our summarization prompt \n",
    "    if summary:\n",
    "        \n",
    "        # A summary already exists\n",
    "        summary_message = (\n",
    "            f\"This is summary of the conversation to date: {summary}\\n\\n\"\n",
    "            \"Extend the summary by taking into account the new messages above:\"\n",
    "        )\n",
    "        \n",
    "    else:\n",
    "        summary_message = \"Create a summary of the conversation above:\"\n",
    "\n",
    "    # Add prompt to our history\n",
    "    messages = state[\"messages\"] + [HumanMessage(content=summary_message)]\n",
    "    response = model.invoke(messages)\n",
    "    \n",
    "    # Delete all but the 2 most recent messages\n",
    "    delete_messages = [RemoveMessage(id=m.id) for m in state[\"messages\"][:-2]]\n",
    "    return {\"summary\": response.content, \"messages\": delete_messages}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f982993e-f4be-4ff7-9a38-886f75398b3d",
   "metadata": {},
   "source": [
    "We'll add a conditional edge to determine whether to produce a summary based on the conversation length."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b507665d-7f5d-442a-b498-218c94c5dd8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import END\n",
    "from typing_extensions import Literal\n",
    "# Determine whether to end or summarize the conversation\n",
    "def should_continue(state: State) -> Literal [\"summarize_conversation\",END]:\n",
    "    \n",
    "    \"\"\"Return the next node to execute.\"\"\"\n",
    "    \n",
    "    messages = state[\"messages\"]\n",
    "    \n",
    "    # If there are more than six messages, then we summarize the conversation\n",
    "    if len(messages) > 6:\n",
    "        return \"summarize_conversation\"\n",
    "    \n",
    "    # Otherwise we can just end\n",
    "    return END"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a838f4c-7067-4f7f-a4c4-6654e11214cd",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "## Adding memory\n",
    "\n",
    "Recall that [state is transient](https://github.com/langchain-ai/langgraph/discussions/352#discussioncomment-9291220) to a single graph execution.\n",
    "\n",
    "This limits our ability to have multi-turn conversations with interruptions. \n",
    "\n",
    "As introduced at the end of Module 1, we can use [persistence](https://docs.langchain.com/oss/python/langgraph/persistence) to address this! \n",
    " \n",
    "LangGraph can use a checkpointer to automatically save the graph state after each step.\n",
    "\n",
    "This built-in persistence layer provides memory, allowing LangGraph to resume from the last state update. \n",
    "\n",
    "As we previously showed, one of the easiest to work with is `MemorySaver`, an in-memory key-value store for Graph state.\n",
    "\n",
    "All we need to do is compile the graph with a checkpointer, and our graph has memory!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "1d57516d-f9f1-4d3c-a84a-7277b5ce6df6",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.graph import StateGraph, START\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(State)\n",
    "workflow.add_node(\"conversation\", call_model)\n",
    "workflow.add_node(summarize_conversation)\n",
    "\n",
    "# Set the entrypoint as conversation\n",
    "workflow.add_edge(START, \"conversation\")\n",
    "workflow.add_conditional_edges(\"conversation\", should_continue)\n",
    "workflow.add_edge(\"summarize_conversation\", END)\n",
    "\n",
    "# Compile\n",
    "memory = MemorySaver()\n",
    "graph = workflow.compile(checkpointer=memory)\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "d0bd5d23-ac3b-4496-a049-9a9f97d2feb9",
   "metadata": {},
   "source": [
    "## Threads\n",
    "\n",
    "The checkpointer saves the state at each step as a checkpoint.\n",
    "\n",
    "These saved checkpoints can be grouped into a `thread` of conversation.\n",
    "\n",
    "Think about Slack as an analog: different channels carry different conversations.\n",
    "\n",
    "Threads are like Slack channels, capturing grouped collections of state (e.g., conversation).\n",
    "\n",
    "Below, we use `configurable` to set a thread ID.\n",
    "\n",
    "![state.jpg](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbadf3b379c2ee621adfd1_chatbot-summarization1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2566c93b-13e6-4a53-bc0f-b00fff691d30",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hello Lance! How can I assist you today?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "You mentioned that your name is Lance. How can I help you today?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "That's great! The San Francisco 49ers have a rich history and a passionate fan base. Do you have a favorite player or a memorable game that you particularly enjoyed?\n"
     ]
    }
   ],
   "source": [
    "# Create a thread\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Start conversation\n",
    "input_message = HumanMessage(content=\"hi! I'm Lance\")\n",
    "output = graph.invoke({\"messages\": [input_message]}, config) \n",
    "for m in output['messages'][-1:]:\n",
    "    m.pretty_print()\n",
    "\n",
    "input_message = HumanMessage(content=\"what's my name?\")\n",
    "output = graph.invoke({\"messages\": [input_message]}, config) \n",
    "for m in output['messages'][-1:]:\n",
    "    m.pretty_print()\n",
    "\n",
    "input_message = HumanMessage(content=\"i like the 49ers!\")\n",
    "output = graph.invoke({\"messages\": [input_message]}, config) \n",
    "for m in output['messages'][-1:]:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "531e5b63-5e8b-486e-baa0-a45521e2fbc2",
   "metadata": {},
   "source": [
    "Now, we don't yet have a summary of the state because we still have < = 6 messages.\n",
    "\n",
    "This was set in `should_continue`. \n",
    "\n",
    "```\n",
    "    # If there are more than six messages, then we summarize the conversation\n",
    "    if len(messages) > 6:\n",
    "        return \"summarize_conversation\"\n",
    "```\n",
    "\n",
    "We can pick up the conversation because we have the thread."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "91b82aaa-17f9-49e2-9528-f4b22e23ebcb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(config).values.get(\"summary\",\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "068a93e9-f716-4980-8edf-94115017d865",
   "metadata": {},
   "source": [
    "The `config` with thread ID allows us to proceed from the previously logged state!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "24b34f0f-62ef-4008-8e96-480cbe92ea3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Yes, as of September 2023, Nick Bosa became the highest-paid defensive player in NFL history. He signed a five-year contract extension with the San Francisco 49ers worth $170 million, with $122.5 million guaranteed. Bosa is known for his exceptional skills as a defensive end and has been a key player for the 49ers.\n"
     ]
    }
   ],
   "source": [
    "input_message = HumanMessage(content=\"i like Nick Bosa, isn't he the highest paid defensive player?\")\n",
    "output = graph.invoke({\"messages\": [input_message]}, config) \n",
    "for m in output['messages'][-1:]:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "22f1b35f-e4bb-47f6-87b1-d84d8aed9aa9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Lance introduced himself and mentioned that he is a fan of the San Francisco 49ers, specifically highlighting his admiration for Nick Bosa. The conversation noted that as of September 2023, Nick Bosa became the highest-paid defensive player in NFL history with a five-year, $170 million contract extension with the 49ers.'"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(config).values.get(\"summary\",\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad7cc0ab-905a-4037-b7cb-69db5b89591e",
   "metadata": {},
   "source": [
    "## LangSmith\n",
    "\n",
    "Let's review the trace!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
